package edu.unice.simul.modele.contenus.mobiles;

import edu.unice.simul.modele.Piece;
import edu.unice.simul.modele.contenus.Contenu;
import edu.unice.simul.modele.contenus.immobiles.Mur;
import edu.unice.simul.modele.enums.Age;
import edu.unice.simul.modele.enums.Sociabilite;
import edu.unice.simul.util.Dim;
import edu.unice.simul.util.Direction;

/**
 * Classe modélisant un humain en tant que contenu d'une pièce
 * 
 * @author Romain Ciaccafava
 * 
 */
public class Humain extends Contenu {
	/**
	 * État actuel de l'humain
	 */
	private EtatHumain etatCourant = new EtatHumain();
	/**
	 * Pièce dans laquelle l'humain évolue actuellement. On a besoin de cette
	 * référence pour effectuer des tests, notamment sur les placements illégaux
	 * de l'humain.
	 */
	private final Piece piece;
	/**
	 * Temps au bout duquel l'humain est mort.
	 */
	private double tempsMort = 0.;
	/**
	 * Âge de l'humain
	 */
	private Age age = Age.Adulte;
	/**
	 * Sociabilité de l'humain
	 */
	private Sociabilite sociabilite = Sociabilite.Modere;

	/**
	 * Créé un humain comme contenu d'une pièce
	 * 
	 * @param pos
	 *          coordonnées auxquelles l'humain est positionné
	 * @param dir
	 *          direction de l'humain
	 * @param piece
	 *          pièce dans laquelle l'humain évolue
	 */
	public Humain(Dim pos, Direction dir, Piece piece) {
		super(pos, dir);
		this.piece = piece;
		for (Contenu contenu : piece.getContenuAt(pos))
			if (contenu instanceof Mur)
				throw new IllegalArgumentException("Position interdite");
		etatCourant.getCoordonneeReele().setX(pos.getX());
		etatCourant.getCoordonneeReele().setY(pos.getY());
		etatCourant.getAncienneCoordonneeReele().setX(pos.getX());
		etatCourant.getAncienneCoordonneeReele().setY(pos.getY());
		etatCourant.setDt2SurM(this.getDeltaT() * this.getDeltaT()
				/ this.getMasse());
		etatCourant.setDtSurTau(this.getDeltaT() * this.getAgressivite());
		etatCourant.setDt2SurTau(this.getDeltaT() * etatCourant.getDtSurTau());
	}

	/**
	 * Créé un humain avec une direction non définie.
	 * 
	 * @param pos
	 *          coordonnées auxquelles l'humain est positionné
	 * @param piece
	 *          pièce dans laquelle l'humain évolue
	 */
	public Humain(Dim pos, Piece piece) {
		this(pos, new Direction(), piece);
	}

	/**
	 * Renvoie la pièce dans laquelle évolue l'humain
	 */
	public Piece getPiece() {
		return this.piece;
	}

	private double getDeltaT() {
		return etatCourant.getDeltaT();
	}

	public double getDtSurTau() {
		return etatCourant.getDtSurTau();
	}

	public double getDt2SurM() {
		return etatCourant.getDt2SurM();
	}

	public double getDt2SurTau() {
		return etatCourant.getDt2SurTau();
	}

	/**
	 * L'humain est-il en vie ?
	 */
	public boolean enVie() {
		return getPrcVie() > 0.;
	}

	/**
	 * Renvoie la taille de la pièce dans laquelle l'humain évolue
	 */
	public Dim getTaillePiece() {
		return piece.getDimmensions();
	}

	/**
	 * Renvoie l'agressivité de l'humain
	 */
	public double getAgressivite() {
		return etatCourant.getAgressivite();
	}

	/**
	 * Définie l'agressivité de l'humain
	 */
	public void setAgressivite(double agressivite) {
		etatCourant.setAgressivite(agressivite);
	}

	/**
	 * Recupère la vitesse souhaitee par l'humain
	 */
	public double getVitesseSouhaitee() {
		return etatCourant.getVitesseSouhaitee();
	}

	/**
	 * Définie la vitesse souhaitee par l'humain
	 */
	public void setVitesseSouhaitee(double vitesseSouhaitee) {
		etatCourant.setVitesseSouhaitee(vitesseSouhaitee);
	}

	/**
	 * Récupère la direction souhaîtée par l'humain
	 */
	public double getDirectionSouhaitee() {
		return etatCourant.getDirectionSouhaitee();
	}

	/**
	 * Définie la direction souhaîtée par l'humain
	 */
	public void setDirectionSouhaitee(double directionSouhaitee) {
		etatCourant.setDirectionSouhaitee(directionSouhaitee);
	}

	/**
	 * Défini que l'humain est sorti de la pièce.
	 * 
	 * @param temps
	 *          temps de sortie de l'humain
	 * @param porteDeSortie
	 *          coordonnées de la porte de sortie empruntée par l'humain
	 */
	public void setSorti(double temps, Dim porteDeSortie) {
		etatCourant.setSorti(temps, porteDeSortie);
	}

	/**
	 * L'humain est-il sorti ?
	 */
	public boolean estSorti() {
		return etatCourant.estSorti();
	}

	/**
	 * Renvoie les coordonnées de la porte empruntée par l'humain pour sortir
	 */
	public Dim getPorteDeSortie() {
		return etatCourant.getPorteDeSortie();
	}

	/**
	 * Change de direction et de vitesse aléatoirement.
	 */
	public void ballade() {
		if (Math.random() < 0.03)
			etatCourant.setDirectionSouhaitee(etatCourant.getDirectionSouhaitee()
					+ Math.PI * (0.5 - Math.random()));
		else
			etatCourant.setDirectionSouhaitee(etatCourant.getDirectionSouhaitee()
					+ Math.PI * (0.5 - Math.random()) / 10);
		if (Math.random() < 0.1)
			etatCourant.setVitesseSouhaitee(etatCourant.getVitesseMax() / 3
					+ Math.random());
	}

	/**
	 * Renseigne le temps de sortie de l'humain
	 */
	public double getTempsDeSortie() {
		return etatCourant.getTempsDeSortie();
	}

	/**
	 * Renvoie l'angle de vision de l'humain
	 */
	public double getAngleVision() {
		return etatCourant.getAngleVision();
	}

	/**
	 * Renvoie la distance de vision de l'humain
	 */
	public double getDistanceVision() {
		return etatCourant.getCourtoisie();
	}

	public double getForceContactX() {
		return etatCourant.getForceContactX();
	}

	public void setForceContactX(double forceContactX) {
		etatCourant.setForceContactX(forceContactX);
	}

	public double getForceContactY() {
		return etatCourant.getForceContactY();
	}

	public void setForceContactY(double forceContactY) {
		etatCourant.setForceContactY(forceContactY);
	}

	/**
	 * Diminue la vie de l'humain.
	 * 
	 * @param prc
	 *          pourcentage de vie à enlever
	 */
	public void diminuerVie(double prc) {
		etatCourant.diminuerVie(prc);
	}

	/**
	 * Diminue la vie de l'humain fonction publique pour les tests
	 * 
	 * @param prc
	 *          pourcentage de vie à enlever
	 */
	public void setAugmenterStress(double prc) {
		etatCourant.augmenterStress(prc);
	}

	/**
	 * Retourne le pourcentage de vie de l'humain.
	 * 
	 * @return Pourcentage de vie de l'humain
	 */
	public double getPrcVie() {
		return etatCourant.getPrcVie();
	}

	/**
	 * Renvoie la masse de l'humain
	 */
	public int getMasse() {
		return etatCourant.getMasse();
	}

	/**
	 * Renvoie le pourcentage de stress de l'humain
	 */
	public double getPrcStress() {
		return etatCourant.getPrcStress();
	}

	/**
	 * Renvoie la vitesse maximale de l'humain en km/h
	 */
	public int getVitesseMax() {
		return etatCourant.getVitesseMax();
	}

	/**
	 * Définie la vitesse maximale de l'humain en km/h
	 */
	public void setVitesseMax(int vitesseMax) {
		etatCourant.setVitesseMax(vitesseMax);
	}

	/**
	 * Renvoie l'abscisse de l'humain
	 */
	public double getAbscisse() {
		return etatCourant.getCoordonneeReele().getX();
	}

	/**
	 * Définie l'abscisse de l'humain
	 */
	public void setAbscisse(double abscisse) {
		if (abscisse >= piece.getDimmensions().getX())
			throw new IllegalArgumentException(
					"Impossible de positionner un humain en dehors de la pièce.");
		etatCourant.getCoordonneeReele().setX(abscisse);
		setPosition(new Dim((int) abscisse, getPosition().getY()));
	}

	/**
	 * Renvoie l'ordonnée de l'humain
	 */
	public double getOrdonnee() {
		return etatCourant.getCoordonneeReele().getY();
	}

	/**
	 * Définie l'ordonnée de l'humain
	 */
	public void setOrdonnee(double ordonnee) {
		if (ordonnee >= piece.getDimmensions().getY())
			throw new IllegalArgumentException(
					"Impossible de positionner un humain en dehors de la pièce.");
		etatCourant.getCoordonneeReele().setY(ordonnee);
		setPosition(new Dim(getPosition().getX(), (int) ordonnee));
	}

	/**
	 * Renvoie l'ancienne abscisse de l'humain
	 */
	public double getAncienAbs() {
		return etatCourant.getAncienneCoordonneeReele().getX();
	}

	/**
	 * Définie l'ancienne abscisse de l'humain
	 */
	public void setAncienAbs(double oldAbs) {
		etatCourant.getAncienneCoordonneeReele().setX(oldAbs);
	}

	/**
	 * Renvoie l'ancienne ordonnée de l'humain
	 */
	public double getAncienOrd() {
		return etatCourant.getAncienneCoordonneeReele().getY();
	}

	/**
	 * Définie l'ancienne ordonnée de l'humain
	 */
	public void setAncienOrd(double oldOrd) {
		etatCourant.getAncienneCoordonneeReele().setY(oldOrd);
	}

	/**
	 * Renvoie la direction de l'humain
	 */
	@Override
	public Direction getDirection() {
		Direction dir = super.getDirection();
		dir.setDirection(Math.atan2((this.getOrdonnee() - this.getAncienOrd()),
				(this.getAbscisse() - this.getAncienAbs())));
		return dir;
	}

	/**
	 * Renvoie le temps au bout duquel l'humain est mort
	 */
	public double getTempsMort() {
		return this.tempsMort;
	}

	/**
	 * Définie le temps au bout duquel l'humain est mort
	 */
	public void setTempsMort(double temps) {
		this.tempsMort = temps;
	}

	/**
	 * Renvoie l'âge de l'humain
	 */
	public Age getAge() {
		return age;
	}

	/**
	 * Renvoie la sociabilité de l'humain
	 */
	public Sociabilite getSociabilite() {
		return sociabilite;
	}

	/**
	 * Définie l'âge de l'humain
	 */
	public void setAge(Age age) {
		this.age = age;
		setVitesseMax(age.getVitesseMax());
		etatCourant.setAngleVision(age.getAngleVision());
		etatCourant.setMasse(age.getMasse());
	}

	/**
	 * Définie la sociabilité de l'humain
	 */
	public void setSociabilite(Sociabilite sociabilite) {
		this.sociabilite = sociabilite;
		setAgressivite(sociabilite.getValue());
	}

	/**
	 * Définie le pourcentage de vie de l'humain
	 */
	public void setPrcVie(double vie) {
		if (vie >= 0 && vie <= 100)
			this.etatCourant.setPrcVie(vie);
	}

	/**
	 * Renvoie la courtoisie de l'humain
	 */
	public double getCourtoisie() {
		return etatCourant.getCourtoisie();
	}
}